/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						       player.cpp

	$Header: /game/player.cpp $

	$NoKeywords: $

 ************************************************************************/

// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "player.h"

#include <algorithm>
#include <limits>

#include "adaptor_handler.h"
#include "adv_dwelling.h"
#include "adventure_ai.h"
#include "adventure_enemy_marker.h"
#include "adventure_map.h"
#include "ai_town_data_cache.h"
#include "ai_importance.h"
#include "ai_value.h"
#include "army.h"
#include "army_array.h"
#include "artifact_properties.h"
#include "bitset_io.h"
#include "building_traits.h"
#include "caravan.h"
#include "creature_traits.h"
#include "creature_type.h"
#include "default_artifact_properties.h"
#include "enum_operations.h"
#include "external_string.h"
#include "format_string.h"
#include "function_handler.h"
#include "hero.h"
#include "ownable_garrisonable_adv_object.h"
#include "map_renderer.h"
#include "material_names.h"
#include "mine.h"
#include "player_color.h"
#include "quest_site.h"
#include "random.h"
#include "replace_keywords.h"
#include "sanctuary.h"
#include "town.h"
#include "town_list.h"
#include "town_type.h"
#include "vector_set.h"

// ------------------------------------------------------------------
// Unnamed namespace
// ------------------------------------------------------------------
namespace
{

	// ------------------------------------------------------------------
	// ------------------------------------------------------------------
	void add_creature_stacks_to_set( t_creature_array & army, t_player::t_creature_stack_set & set )
	{
		int slot_num;
		for ( slot_num = 0; slot_num < t_creature_array::k_size; ++slot_num )
		{
			if ( army[ slot_num ].get_number() > 0 )
				set.insert( &army[ slot_num ] );
		}
	}

} // Unnamed namespace

extern  t_external_string const k_player_income_text( "player_income.text" );

static int const k_initial_budget[2][k_difficulty_count][k_material_count] =
{
	// human starting budget
	{   // gold  wood  ore  crystal sulfur mercury gems 
		{ 20000,  20,  20,    10,     10,    10,    10 },
		{ 15000,  15,  15,     7,      7,     7,     7 },
		{ 10000,  10,  10,     5,      5,     5,     5 },
		{  5000,  10,  10,     5,      5,     5,     5 },
		{     0,  10,  10,     5,      5,     5,     5 }
	}, // computer starting budget
	{  // gold  wood  ore  crystal sulfur mercury gems  
		{     0,  10,  10,     5,      5,     5,     5 },
		{  5000,  10,  10,     5,      5,     5,     5 },
		{ 10000,  10,  10,     5,      5,     5,     5 },
		{ 15000,  15,  15,     7,      7,     7,     7 },
		{ 20000,  20,  20,    10,     10,    10,    10 },
	}
};

t_material_array get_initial_budget( t_difficulty difficulty )
{
	int					i;
	t_material_array	result;

	for (i = 0; i < k_material_count; ++i)
		result[i] = k_initial_budget[false][difficulty][i];
	return result;
}

// ------------------------------------------------------------------
// player data structure 
// ------------------------------------------------------------------
t_player::t_player()  : m_computer( false ), m_team( 0 )
{
	common_construct();
}

// ------------------------------------------------------------------
// player data structure 
// ------------------------------------------------------------------
t_player::t_player( bool computer, int team ) : m_computer( computer ), m_team( team )
{
	common_construct();
}

// ------------------------------------------------------------------
// Common code for both constructors
// ------------------------------------------------------------------
void t_player::common_construct()
{
    int i;

	m_lighthouse_count = 0;
	m_armies = new t_army_array;
	m_graveyards = new t_army_array;
	m_towns = new t_town_list;
	m_alignment = k_town_none;
	m_color = random( k_active_player_color_count );
	m_has_max_level = false;
	m_max_level = std::numeric_limits< t_uint16 >::max();
	for (i = 0; i < k_border_guard_color_count; i++)
	    m_key[i] = false;
	for (i = 0; i < k_obelisk_color_count; i++)
	    m_obelisk_count[i] = 0;
	m_quests_completed = 0;
	m_battle_points = 0;
	m_battles_won = 0;
	m_ai_importance = k_ai_importance_normal;
	m_eliminated = false;
	m_force_elimination = false;
}

// ------------------------------------------------------------------
// Note new caravan
// ------------------------------------------------------------------
void t_player::add( t_caravan * caravan )
{
#ifndef NDEBUG
	t_caravan_set::const_iterator it;
	for ( it = m_caravans.begin(); it != m_caravans.end(); it++ )
	{
		assert( caravan != *it );
		assert( (*it)->get_owner() == this );
	}
	assert( caravan->get_owner() == this );
#endif

	m_caravans.insert( caravan );
}


// ------------------------------------------------------------------
// Note ownership of dwelling
// ------------------------------------------------------------------
void t_player::add( t_adv_dwelling * dwelling )
{
#ifndef NDEBUG
	t_adv_dwelling_list::const_iterator it;
	for ( it = m_dwelling_list.begin(); it != m_dwelling_list.end(); it++ )
	{
		assert( dwelling != *it );
		assert( (*it)->get_owner() == this );
	}
	assert( dwelling->get_owner() == this );
#endif

	m_dwelling_list.push_back( dwelling );
}

// ------------------------------------------------------------------
// player data structure 
// ------------------------------------------------------------------
void t_player::add_army( t_army* army )
{
	assert( !army->is_graveyard() );

#ifndef NDEBUG
	t_army_array::const_iterator it;
	for ( it = m_armies->begin(); it != m_armies->end(); it++ )
	{
		assert( army != *it );
		assert( (*it)->get_owner() == this );
	}
	assert( army->get_owner() == this );
#endif

	m_armies->push_back( army );
}

// ------------------------------------------------------------------
// player data structure 
// ------------------------------------------------------------------
void t_player::add_graveyard( t_army* army )
{
	assert( army->is_graveyard() );

#ifndef NDEBUG
	t_army_array::const_iterator it;
	for ( it = m_graveyards->begin(); it != m_graveyards->end(); it++ )
	{
		assert( army != *it );
		assert( (*it)->get_owner() == this );
	}
	assert( army->get_owner() == this );
#endif

	m_graveyards->push_back( army );
}

// ------------------------------------------------------------------
// Note ownership of garrison
// ------------------------------------------------------------------
void t_player::add_garrison( t_ownable_garrisonable_adv_object * garrison )
{
#ifndef NDEBUG
	t_ownable_garrisonable_adv_object_list::const_iterator it;
	for ( it = m_owned_garrisons.begin(); it != m_owned_garrisons.end(); it++ )
	{
		assert( garrison != *it );
		assert( (*it)->get_owner() == this );
	}
	assert( garrison->get_owner() == this );
#endif

	m_owned_garrisons.push_back( garrison );
}

void t_player::add( t_quest_origin_site* quest_site )
{
	m_quest_origin_sites.push_back( quest_site );
}

// ------------------------------------------------------------------
// player data structure 
// ------------------------------------------------------------------
t_player::t_creature_stack_set t_player::build_creature_stack_set() const
{
	t_creature_stack_set result;

	t_army_array::const_iterator army_end = m_armies->end();
	t_army_array::const_iterator army_iter = m_armies->begin();
	for ( ; army_iter != army_end; ++army_iter )
		add_creature_stacks_to_set( **army_iter, result );

	army_end = m_graveyards->end();
	army_iter = m_graveyards->begin();
	for ( ; army_iter != army_end; ++army_iter )
		add_creature_stacks_to_set( **army_iter, result );

	t_town_list::const_iterator town_end = m_towns->end();
	t_town_list::const_iterator town_iter = m_towns->begin();
	for ( ; town_iter != town_end; ++town_iter )
		add_creature_stacks_to_set( **town_iter, result );

	t_ownable_garrisonable_adv_object_list::const_iterator garrison_end = m_owned_garrisons.end();
	t_ownable_garrisonable_adv_object_list::const_iterator garrison_iter = m_owned_garrisons.begin();
	for ( ; garrison_iter != garrison_end; ++garrison_iter )
		add_creature_stacks_to_set( **garrison_iter, result );

	t_caravan_set::const_iterator caravan_end = m_caravans.end();
	t_caravan_set::const_iterator caravan_iter = m_caravans.begin();
	for ( ; caravan_iter != caravan_end; ++caravan_iter )
		add_creature_stacks_to_set( **caravan_iter, result );

	return result;
}

// ------------------------------------------------------------------
// player data structure 
// ------------------------------------------------------------------
void t_player::clear_max_level()
{
	m_has_max_level = false;
	m_max_level = std::numeric_limits< t_uint16 >::max();
}

// ------------------------------------------------------------------
// player data structure 
// ------------------------------------------------------------------
t_army_array* t_player::get_armies() 
{
	return m_armies;
}

// ------------------------------------------------------------------
// player data structure 
// ------------------------------------------------------------------
t_army_array const* t_player::get_armies() const
{
	return m_armies;
}

// ------------------------------------------------------------------
// player data structure 
// ------------------------------------------------------------------
void t_player::add_object( t_adventure_object* object )
{
#ifndef NDEBUG
	std::vector<t_adventure_object_ptr>::const_iterator it;
	for ( it = m_owned_objects.begin(); it != m_owned_objects.end(); it++ )
	{
		assert( object != *it );
		assert( (*it)->get_owner() == this );
	}
	assert( object->get_owner() == this );
#endif

	m_owned_objects.push_back( object );
}

// ------------------------------------------------------------------
// player data structure 
// ------------------------------------------------------------------
void t_player::add_town( t_town* town )
{
#ifndef NDEBUG
	t_town_list::const_iterator it;
	for ( it = m_towns->begin(); it != m_towns->end(); it++ )
	{
		assert( town != *it );
		assert( (*it)->get_owner() == this );
	}
	assert( town->get_owner() == this );
#endif

	m_towns->push_back( town );
}

// ------------------------------------------------------------------
// player data structure 
// ------------------------------------------------------------------
void t_player::add( t_mine* mine )
{
#ifndef NDEBUG
	std::vector<t_mine_ptr>::const_iterator it;
	for ( it = m_mines.begin(); it != m_mines.end(); it++ )
	{
		assert( mine != *it );
		assert( (*it)->get_owner() == this );
	}
	assert( mine->get_owner() == this );
#endif

	m_mines.push_back( mine );
}

t_town_list* t_player::get_towns()
{
	return m_towns;
}

// ------------------------------------------------------------------
// player data structure 
// add a fractional amount of material to the hero
// ------------------------------------------------------------------
void t_player::fractional_gain( t_material material, int amount )
{
	m_fractional_funds[material] += amount;
	if (m_fractional_funds[material] >= 256)
	{
		m_funds[material] += m_fractional_funds[material] >> 8;
		m_fractional_funds[material] &= 0xFF;
	}
}

// ------------------------------------------------------------------
// player data structure 
// ------------------------------------------------------------------
void t_player::initialize( bool computer_budget, t_difficulty difficulty )
{
	int  i;

	for (i = 0; i < k_material_count; i++)
		m_funds[i] = k_initial_budget[computer_budget][difficulty][i];
	for (i = 0; i < k_border_guard_color_count; i++)
	    m_key[i] = false;
}

/*
** Set key.
*/
void t_player::set_key( int key, bool set )
{
	assert( key < k_border_guard_color_count );

    m_key[key] = set;    
}

/*
** Check if hero has key.
*/
bool t_player::has_key( int key ) const
{
	assert( key < k_border_guard_color_count );

    return m_key[key];
}

// Number of this obelisk player has visited.
int t_player::get_obelisk_count (t_obelisk_color color) const
{
    return (m_obelisk_count[ color ]);
}

void t_player::inc_obelisk_count (t_obelisk_color color)
{
    m_obelisk_count[ color ]++;
}

// Helper function for t_player::get_income()
static void add_creature_array_income( t_creature_array const & creatures, 
									   t_material_array&		income,
									   int						day)
{
	int i;
    for (i = 0; i < t_creature_array::k_size; i++)
    {
		if (creatures[i].get_number() == 0)
			continue;

		creatures[i].get_income( day, income );
    }
}

// Get player's daily income.
t_material_array    t_player::get_income( int day ) const
{
    t_material_array income;
    
    int i;

	for (i = 0; i<k_material_count; i++)
    {
	    income[i] = 0;
    }

    // Mines.
	for (i=0; i<m_mines.size(); i++)
    {
	    income[ m_mines[i]->get_material() ] += m_mines[i]->get_production();
    }

    // Towns.
	t_town_list::const_iterator town_index;
	for (town_index = m_towns->begin(); town_index != m_towns->end(); town_index++)
	{
	    income[ k_gold ] += ((*town_index)->get_gold_production());
    }

	// Armies & other creature arrays
    for (i=0; i<m_armies->size(); i++)
    {
	    t_army* army_ptr = (*m_armies)[i];
	    t_creature_array&    creatures = army_ptr->get_creatures();

		add_creature_array_income( creatures, income, day );
	}

	t_ownable_garrisonable_adv_object_list::const_iterator garrison_iter;

	for ( garrison_iter = m_owned_garrisons.begin(); garrison_iter != m_owned_garrisons.end(); garrison_iter++ )
	{
		add_creature_array_income( **garrison_iter, income, day );
	}

	return income;
}

// Get player's daily income.
void t_player::get_income_text( int day, int material, std::string &income_text )
{
    t_material_array    town_income;
    t_material_array    creature_income;
    t_material_array    garrison_income;
    int                 i;

    //m_income_text = k_player_income_text.c_str();

	for (i = 0; i<k_material_count; i++)
    {
	    creature_income[i] = 0;
	    garrison_income[i] = 0;
    }

    income_text = replace_keywords( k_player_income_text, "%material", k_material_name[material] );

    // Mines.
    int mine_income = 0;
	for (i=0; i<m_mines.size(); i++)
    {
	    if (m_mines[i]->get_material() == material)
        {
            mine_income += m_mines[i]->get_production();
        }
    }
    income_text = replace_keywords( income_text.c_str(), "%mine_income", format_string("%d\n", mine_income)  );

    // Towns.
	t_town_list::const_iterator town_index;
	for (town_index = m_towns->begin(); town_index != m_towns->end(); town_index++)
	{
	    town_income[ k_gold ] += ((*town_index)->get_gold_production());
    }
    income_text = replace_keywords( income_text.c_str(), "%town_income", format_string("%d\n", town_income[material]) );

	// Armies & other creature arrays
    for (i=0; i<m_armies->size(); i++)
    {
	    t_army* army_ptr = (*m_armies)[i];
	    t_creature_array&    creatures = army_ptr->get_creatures();

		add_creature_array_income( creatures, creature_income, day );
	}
    income_text = replace_keywords( income_text.c_str(), "%creature_income", format_string("%d\n", creature_income[material]) );

	t_ownable_garrisonable_adv_object_list::const_iterator garrison_iter;
	for ( garrison_iter = m_owned_garrisons.begin(); garrison_iter != m_owned_garrisons.end(); garrison_iter++ )
	{
		add_creature_array_income( **garrison_iter, garrison_income, day );
	}
    income_text = replace_keywords( income_text.c_str(), "%garrison_income", format_string("%d", garrison_income[material]) );

}

// ------------------------------------------------------------------
// player data structure 
// Does this player own a town of the given alignment with the 
// Grail Building?
// ------------------------------------------------------------------
bool t_player::gets_global_grail_effects( t_town_type alignment ) const
{
	std::vector<t_town_ptr>::const_iterator index;

	for ( index = m_towns->begin(); index != m_towns->end(); index++ )
	{
		if ( (*index)->get_type() == alignment && (*index)->has( k_town_grail ) )
			return true;
	}

	return false;
}

// ------------------------------------------------------------------
// player data structure 
// ------------------------------------------------------------------
bool t_player::can_create_army() const
{
	return m_armies->size() < k_player_army_limit;
}


// ------------------------------------------------------------------
// Note player no longer owns dwelling
// ------------------------------------------------------------------
void t_player::remove( t_adv_dwelling * dwelling )
{
	t_adv_dwelling_list::iterator index = std::find( m_dwelling_list.begin(), m_dwelling_list.end(), t_adv_dwelling_ptr( dwelling ) );

	if ( index == m_dwelling_list.end() )
	{
		assert( false );
		return;
	}
	m_dwelling_list.erase( index );
}

// ------------------------------------------------------------------
// player data structure 
// ------------------------------------------------------------------
void t_player::remove_object( t_adventure_object* object )
{
	std::vector<t_adventure_object_ptr>::iterator index;

	index = std::find( m_owned_objects.begin(), m_owned_objects.end(), t_adventure_object_ptr(object) );
	if (index == m_owned_objects.end())
	{
		assert( false );
		return;
	}
	m_owned_objects.erase( index );
}

// ------------------------------------------------------------------
// player data structure 
// ------------------------------------------------------------------
void t_player::remove_army( t_army* army )
{
	std::vector<t_army_ptr>::iterator index;

	index = std::find( m_armies->begin(), m_armies->end(), t_army_ptr(army) );
	if (index == m_armies->end())
	{
		assert( false );
		return;
	}
	m_armies->erase( index );
}

// ------------------------------------------------------------------
// player data structure 
// ------------------------------------------------------------------
void t_player::remove_graveyard( t_army* army )
{
	std::vector<t_army_ptr>::iterator index;

	index = std::find( m_graveyards->begin(), m_graveyards->end(), t_army_ptr(army) );
	if (index == m_graveyards->end())
	{
		assert( false );
		return;
	}
	m_graveyards->erase( index );
}

// ------------------------------------------------------------------
// Caravan is going away
// ------------------------------------------------------------------
void t_player::remove( t_caravan * caravan )
{
	t_caravan_set::iterator index;

	index = m_caravans.find( caravan );
	while ( index != m_caravans.end() && index->get() != caravan )
		index++;

	if (index == m_caravans.end())
	{
		assert( false );
		return;
	}

	m_caravans.erase( index );
}

// ------------------------------------------------------------------
// player data structure 
// ------------------------------------------------------------------
void t_player::remove( t_mine* mine )
{
	std::vector<t_mine_ptr>::iterator index;

	index = std::find( m_mines.begin(), m_mines.end(), t_mine_ptr(mine) );
	if (index == m_mines.end())
	{
		assert( false );
		return;
	}
	m_mines.erase( index );
}

// ------------------------------------------------------------------
// Note player no longer owns garrison
// ------------------------------------------------------------------
void t_player::remove_garrison( t_ownable_garrisonable_adv_object * garrison )
{
	t_ownable_garrisonable_adv_object_list::iterator index = 
		std::find( m_owned_garrisons.begin(), m_owned_garrisons.end(), t_ownable_garrisonable_adv_object_ptr ( garrison ) );

	if ( index == m_owned_garrisons.end() )
	{
		assert( false );
		return;
	}
	m_owned_garrisons.erase( index );
}


// ------------------------------------------------------------------
// player data structure 
// ------------------------------------------------------------------
void t_player::remove_town( t_town* town )
{
	std::vector<t_town_ptr>::iterator index;

	index = std::find( m_towns->begin(), m_towns->end(), t_town_ptr(town) );
	if (index == m_towns->end())
	{
		assert( false );
		return;
	}
	m_towns->erase( index );
}

void t_player::remove( t_quest_origin_site* quest_site )
{
	std::vector<t_quest_origin_site_ptr>::iterator index;

	index = std::find( m_quest_origin_sites.begin(), m_quest_origin_sites.end(), t_quest_origin_site_ptr(quest_site) );
	if (index == m_quest_origin_sites.end())
	{
		assert( false );
		return;
	}
	m_quest_origin_sites.erase( index );
}

// ------------------------------------------------------------------
// player data structure 
// ------------------------------------------------------------------
void t_player::mark_visibility(
	int						level,
	t_visibility_overlay &	visibility_overlay ) const
{
	typedef std::vector< t_adventure_object_ptr >::const_iterator t_object_iter;
	t_object_iter object_end = m_owned_objects.end();
	t_object_iter object_iter = m_owned_objects.begin();
	for ( ; object_iter != object_end; ++object_iter )
		( *object_iter )->mark_visibility( level, visibility_overlay );
}

int const k_version = 9;

// ------------------------------------------------------------------
// player data structure 
// ------------------------------------------------------------------
bool t_player::write( std::streambuf& buffer ) const
{
    int i;

	put<t_uint16>( buffer, k_version );
	write_string16( buffer, m_name );
	put<t_uint8>( buffer, m_color );
	put<t_uint8>( buffer, m_alignment );
	put<bool>( buffer, m_computer );
	m_funds.write( buffer );
	put<t_uint8>( buffer, m_team );
	for (i = 0; i < k_border_guard_color_count; i++)
	    put< bool >( buffer, m_key[i] );
    for (i = 0; i < k_obelisk_color_count; i++)
	    put< t_uint8 >( buffer, m_obelisk_count[i] );
	put< bool >( buffer, m_has_max_level );
	if ( m_has_max_level )
		put< t_uint16 >( buffer, m_max_level );
	put< t_uint8 >( buffer, m_ai_importance );
	put< bool >( buffer, m_eliminated );
	put< bool >( buffer, m_force_elimination );

	put_bitset( buffer, m_seen_booster_messages );

	return true;
}

// ------------------------------------------------------------------
// player data structure 
// ------------------------------------------------------------------
bool t_player::read( std::streambuf& buffer, int overall_version ) 
{
	int version;
    int i;

	if (overall_version > 0)
		version = get<t_uint16>( buffer );
	else
		version = 0;

	if ( version > k_version )
		return false;

	if (version > 1)
		read_string16( buffer, m_name );
	if (version > 0)
	{
		m_color = t_player_color( get<t_uint8>( buffer ));
		m_alignment = t_town_type( get<t_uint8>( buffer ));
	}
	else
		m_alignment = get<t_town_type>( buffer );
	m_computer = get<bool>( buffer );
	m_funds.read( buffer );
	m_team = get<t_uint8>( buffer );
	if (version < 3)
	{
	    for (i = 0; i < k_border_guard_color_count; i++)
	        m_key[i] = false;
    }
    else
    {
	    for (i = 0; i < k_border_guard_color_count; i++)
	        m_key[i] = (get< bool >( buffer ));
    }
    // Obelisks visited.
	if (version < 4)
    {
	    for (i = 0; i < k_obelisk_color_count; i++)
	        m_obelisk_count[i] = 0;
    }
    else
    {
	    for (i = 0; i < k_obelisk_color_count; i++)
	        m_obelisk_count[i] = (get<t_uint8>(buffer));
    }

	if ( version < 5 )
	{
		m_has_max_level = false;
		m_max_level = std::numeric_limits< t_uint16 >::max();
	}
	else
	{
		m_has_max_level = get< bool >( buffer );
		if ( m_has_max_level )
			m_max_level = get< t_uint16 >( buffer );
		else
			m_max_level = std::numeric_limits< t_uint16 >::max();
	}	

	if ( version < 7 )
	{
		m_ai_importance = k_ai_importance_normal;
	}
	else
	{
		m_ai_importance = t_ai_importance( get< t_uint8 >( buffer ) );
	}

	if (version < 8)
	{
		m_eliminated = false;
		m_force_elimination = false;
	}
	else
	{
		m_eliminated = get< bool >( buffer );
		m_force_elimination = get< bool >( buffer );
	}

	if ( version < 9 )
	{
		m_seen_booster_messages.reset();
	}
	else
	{
		m_seen_booster_messages = get_bitset<k_town_building_count>( buffer );
	}

	return true;
}

// ------------------------------------------------------------------
// player data structure 
// ------------------------------------------------------------------
bool t_player::has_lost() const 
{
	if ( m_eliminated || m_force_elimination ) 
		return true;

	// Do they have caravans in transit or any remaining towns?
	if ( !m_towns->empty() || !m_caravans.empty() )
		return false;
	
	// Ignore armies that are empty except for dead heroes.
	t_army_array::const_iterator army_iter;
	for ( army_iter = m_armies->begin(); army_iter != m_armies->end(); army_iter++)
	{
		if ( (*army_iter)->is_alive() )
			return false;
	}

	// Any armies hiding in sanctuaries?
	t_sanctuary_list::const_iterator sanctuary_iter;
	for ( sanctuary_iter = m_sanctuary_list.begin(); sanctuary_iter != m_sanctuary_list.end(); sanctuary_iter++)
	{
		if ((*sanctuary_iter)->is_alive())
			return false;
	}

	// Do they have garrisons they could pull out?
	t_ownable_garrisonable_adv_object_list::const_iterator garrison_iter;
	for ( garrison_iter = m_owned_garrisons.begin(); garrison_iter != m_owned_garrisons.end(); garrison_iter++ )
	{
		if ((*garrison_iter)->can_remove_garrison() && (*garrison_iter)->is_alive())
			return false;
	}

	return true;
}

// ------------------------------------------------------------------
// tells is the player is newly eliminated from the game or not
// ------------------------------------------------------------------
bool t_player::is_eliminated() const
{
	return m_eliminated;
}

// ------------------------------------------------------------------
// player data structure 
// Get bonus to magic resistance from all magic dampeners
// ------------------------------------------------------------------
int t_player::get_magic_dampener_bonus() const
{
	t_town_list::const_iterator town_index;
	int bonus = 0;

	for (town_index = m_towns->begin(); town_index != m_towns->end(); town_index++)
	{
		if ((*town_index)->has(k_town_magic_dampener))
		{
			bonus += ( 100 - bonus ) / 10;
		}
	}

	return bonus;
}

// ------------------------------------------------------------------
// player data structure 
// Get bonus to necromancy from all necromantic amplifiers
// ------------------------------------------------------------------
int	t_player::get_necromancy_amplifier_bonus() const
{
	t_town_list::const_iterator town_index;
	int bonus = 0;

	for (town_index = m_towns->begin(); town_index != m_towns->end(); town_index++)
	{
		if ((*town_index)->has(k_town_necromancy_amplifier))
		{
			bonus += 10;
		}
	}

	return bonus;
}

// ------------------------------------------------------------------
// Force all town garrisons to visit_town() on their current town
// ------------------------------------------------------------------
void t_player::force_garrison_visits()
{
	t_town_list::const_iterator town_index;

	for (town_index = m_towns->begin(); town_index != m_towns->end(); town_index++)
	{
		(*town_index)->visit_town(*town_index);
	}
}

// Return total player's experience.
int t_player::get_total_army_experience() const
{
    int i;
    int total_experience = 0;

    for (i=0; i<m_armies->size(); i++)
    {
	    t_army* army_ptr = (*m_armies)[i];
	    t_creature_array&    creatures = army_ptr->get_creatures();
        total_experience += creatures.get_experience_value();
    }

	t_town_list::const_iterator town_index;
	for (town_index = m_towns->begin(); town_index != m_towns->end(); town_index++)
	{
        t_creature_array& town_garrison_army = ((*town_index)->get_garrison ());
        total_experience += town_garrison_army.get_experience_value();
    }

	t_ownable_garrisonable_adv_object_list::const_iterator garrison_index;
	for ( garrison_index = m_owned_garrisons.begin(); garrison_index != m_owned_garrisons.end(); garrison_index++ )
    {
        total_experience += (*garrison_index)->get_experience_value();
    }

    for (i=0; i<m_owned_objects.size(); i++)
    {
        t_sanctuary* sanctuary = dynamic_cast<t_sanctuary*> (&(*m_owned_objects[i]));

        if (sanctuary)
            total_experience += sanctuary->get_experience_value();
    }

    return total_experience;
}

// ------------------------------------------------------------------
// ------------------------------------------------------------------
t_ownable_garrisonable_adv_object_list const & t_player::get_garrisons() const
{
	return m_owned_garrisons;
}

// ------------------------------------------------------------------
// ------------------------------------------------------------------
void t_player::ai_start_turn(t_adventure_map& map)
{
	// Calculate the amount of growth, for each level, produced by all of our towns.
	// This figure is the overall value of the weekly production.
	std::fill(&m_creature_growth[0], &m_creature_growth[k_creature_level_count], 0.0f); 
	int count = m_towns->size();
	bool has_festival_of_life = gets_global_grail_effects( k_town_might );
	for (int i = 0; i < count; i++) 
	{
		t_town const* town = (*m_towns)[i];
		t_town_type town_type = town->get_type();
		for (t_town_building building = k_town_dwelling_1; building <= k_town_dwelling_8; enum_incr(building) )
		{
			if (town->has(building)) 
			{
				t_creature_type creature_type = get_traits( town_type, building ).creature;
				t_creature_traits const& traits = get_traits( creature_type );

				float bonus = 1.0f + town->get_nobility_bonus() / 100;
				
				if (town->has( k_town_breeding_pit ))
					bonus += 0.5;
				if ( town->has( k_town_grail ) && town->get_type() == k_town_might )
					bonus += 1.0;
				else if ( has_festival_of_life )
					bonus += 0.25;
				
				m_creature_growth[traits.level] += traits.weekly_growth * traits.experience * bonus;
			}
		}
	}

	ai_calculate_artifact_values();

	ai_calculate_resource_demand(map.get_day());

	ai_calculate_reserved_funds();
}

// ------------------------------------------------------------------
// ------------------------------------------------------------------
void t_player::ai_calculate_resource_demand(int day)
{
	t_material_array	total_demand;

	t_production_options options;

	t_town_list::iterator	town_iter = m_towns->begin(),
							town_iter_end = m_towns->end();
	while (town_iter != town_iter_end)
	{
		t_town_ptr town_ptr = *town_iter++;		
		for (t_town_building i = t_town_building(0); i < k_town_building_count; enum_incr(i))
		{
			if (town_ptr->has(i))
			{
				if ((i >= k_town_dwelling_1) && (i <= k_town_dwelling_8))
				{
					t_building_traits const& traits = get_traits( town_ptr->get_type(), i );

					int dwelling = i - k_town_dwelling_1;
					int population = town_ptr->get_population(dwelling);

					t_creature_type type = traits.creature;

					t_production_options::iterator 
						iter = std::find_if( options.begin(), options.end(), t_find_production_type( type ) );
					if (iter == options.end()) 
					{
						t_creature_traits const& creature_traits = get_traits( type );

						t_production_option desc;
						desc.type = type;
						desc.count = population;
						desc.per_value = creature_traits.experience;
						desc.total_value = desc.count * desc.per_value;
						desc.cost = creature_traits.cost;
						options.push_back(desc);
					}
					else
					{
						t_production_option& desc = *iter;
						desc.count += population;
						desc.total_value += population * desc.per_value;
					}
				}
			} 
			else
				if (town_ptr->can_build(i)) 
				{
					t_building_traits const& traits = get_traits( town_ptr->get_type(), i );
					t_material_array const& cost = traits.cost;
					
					for (int j = 0; j < k_material_count; j++)
					{
						int current_demand = total_demand[j];
						int current_cost = cost[j];
						total_demand[j] = (current_cost > current_demand) ? current_cost : current_demand;
					}
				}
		}
	}

	if (!options.empty())
	{
		std::sort( options.begin(), options.end(), t_production_value_order() );
		int i = 0;
		t_production_options::iterator iter = options.begin(),
									end = options.end();

		while ((iter != end) && (++i <= 3))
		{
			t_production_option& desc = *iter++;
			total_demand += desc.cost * desc.count;
		}
	}
	t_material_array supply = get_funds() + get_income( day ) * 2;
	for (int i = 0; i < k_material_count; i++)
	{
		float virtual_supply = static_cast<float>(supply[i]);
		if (virtual_supply == 0.0f)
			virtual_supply = 1.0f;
		m_material_values[i] = total_demand[i] / virtual_supply;
		if (m_material_values[i] > 3.0f) 
			m_material_values[i] = 3.0f;

		if (m_material_values[i] < 1.0f)
			m_material_values[i] = 1.0f;

		m_material_values[i] *= k_material_value[i];
	}
}

// ------------------------------------------------------------------
// ------------------------------------------------------------------
void t_player::ai_calculate_reserved_funds( int day )
{
	m_ai_reserved_funds.set(0);

	t_production_options options;
	get_production_options( this, options, 2 );

	t_production_options::iterator 
		iter = options.begin(),
		iter_end = options.end();

	while (iter != iter_end) 
	{
		m_ai_reserved_funds += iter->cost * iter->count;
		iter++;
	}
	
	t_material_array funds = m_funds;
	if ( day != -1 )
		funds += get_income( day );

	m_ai_discretionary_funds = funds - m_ai_reserved_funds;
	for (int i = 0; i < k_material_count; i++)
	{
		if (m_ai_discretionary_funds[i] < 0) 
			m_ai_discretionary_funds[i] = 0;

		if (m_ai_discretionary_funds[i] > m_funds[i])
			m_ai_discretionary_funds[i] = m_funds[i];
	}
}

// ------------------------------------------------------------------
// ------------------------------------------------------------------
void t_player::ai_calculate_artifact_values()
{
	int count[k_artifact_type_evaluation_count];
	t_army_array::const_iterator 
		army_itr = m_armies->begin(),
		army_end = m_armies->end();

	std::fill(&m_ai_artifact_value[0], &m_ai_artifact_value[k_artifact_type_evaluation_count], 0);
	std::fill(&count[0], &count[k_artifact_type_evaluation_count], 0);

	while ( army_itr != army_end )
	{
		t_creature_array const& army = (*army_itr++)->get_creatures();

		ai_calculate_artifact_values( army, count );
	}

	t_town_list::const_iterator 
		town_itr = m_towns->begin(),
		town_end = m_towns->end();

	while ( town_itr != town_end )
	{
		t_creature_array const& army = (*town_itr++)->get_garrison();

		ai_calculate_artifact_values( army, count );
	}

	t_ownable_garrisonable_adv_object_list::const_iterator
		garrison_itr = m_owned_garrisons.begin(),
		garrison_end = m_owned_garrisons.end();

	while ( garrison_itr != garrison_end )
	{
		t_creature_array const& army = (*garrison_itr++)->get_garrison();
		
		ai_calculate_artifact_values( army, count );
	}

	for (int j = 0; j <= k_artifact_level_relic; j++)
	{
		if (count[j])
			m_ai_artifact_value[j] = m_ai_artifact_value[j] / count[j];
	}
}

void t_player::ai_calculate_artifact_values( t_creature_array const& army, int* count)
{
	for (int i = 0; i < t_creature_array::k_size; i++)
	{
		t_hero const* hero = army[i].get_const_hero();
		if (!hero || hero->is_dead()) continue;

		for (t_artifact_type type = enum_add(k_artifact_none, 1); type < k_artifact_type_count; enum_incr(type))
		{
			if ( is_cut( type ) )
				continue;

			t_artifact_level level = get_level(type);
			if ((level >= k_artifact_level_item) && (level <= k_artifact_level_relic))
			{
				t_artifact_value_query value( &army, hero, type );
				ai_value_of_artifact( value );
				float actual_value = value.new_value - value.existing_value;
				if (actual_value > 0.0f)
				{
					m_ai_artifact_value[level] += actual_value;
					count[level]++;
				}
			}
		}
	}
}

// ------------------------------------------------------------------
// ------------------------------------------------------------------
t_uint8 t_player::get_building_delay() const
{
	return 1;
}

// ------------------------------------------------------------------
// ------------------------------------------------------------------
t_material_array const& t_player::ai_get_discretionary_funds() const
{
	// This will eventually return the extra funds an ai player can spend without 
	// cutting into his reserves.
	return m_ai_discretionary_funds;
}

// ------------------------------------------------------------------
// ------------------------------------------------------------------
void t_player::ai_set_discretionary_amount( int material, int amount )
{
	m_ai_discretionary_funds[ material ] = amount;
}

// ------------------------------------------------------------------
// ------------------------------------------------------------------
void t_player::ai_set_discretionary_funds( t_material_array const& funds )
{
	m_ai_discretionary_funds = funds;
}

// ------------------------------------------------------------------
// ------------------------------------------------------------------
int	t_player::ai_get_discretionary_amount( int material ) const
{
	return m_ai_discretionary_funds[material];
}

// ------------------------------------------------------------------
// ------------------------------------------------------------------
void t_player::set_material_values( t_material_values const& values)
{
	m_material_values = values;
}

// ------------------------------------------------------------------
// ------------------------------------------------------------------
void t_player::add( t_sanctuary* sanctuary )
{
	m_sanctuary_list.push_back( sanctuary );
}

// ------------------------------------------------------------------
// ------------------------------------------------------------------
void t_player::remove( t_sanctuary* sanctuary )
{
	t_sanctuary_list::iterator index;

	index = std::find( m_sanctuary_list.begin(), m_sanctuary_list.end(), t_sanctuary_ptr(sanctuary) );
	if (index == m_sanctuary_list.end())
	{
		assert( false );
		return;
	}
	m_sanctuary_list.erase( index );
}

// ------------------------------------------------------------------
// ------------------------------------------------------------------
t_sanctuary_list const&	t_player::get_sanctuary_list() const
{
	return m_sanctuary_list;
}

// ------------------------------------------------------------------
// Array containing the ai's demand scaling value for the player.
// ------------------------------------------------------------------
t_material_values const& t_player::get_material_values() const
{
	return m_material_values;
}

// ------------------------------------------------------------------
// material array
// ------------------------------------------------------------------
t_material_array t_player::get_valued_funds( t_material_array const& materials ) const
{
	return m_material_values * materials;
}

// ------------------------------------------------------------------
// ------------------------------------------------------------------
float t_player::get_material_value(int material) const
{
	return m_material_values[material];
}

// ------------------------------------------------------------------
// ------------------------------------------------------------------
float t_player::get_weekly_creature_growth( int level ) const
{
	assert( (level >= 0) && (level < k_creature_level_count) );
	return m_creature_growth[level];
}

// ------------------------------------------------------------------
// ------------------------------------------------------------------
float t_player::ai_get_artifact_value( t_artifact_level level ) const
{
	assert ( (level >= k_artifact_level_item) && (level <= k_artifact_level_relic));
	return m_ai_artifact_value[level];
}
	
// ------------------------------------------------------------------
// ------------------------------------------------------------------
void t_player::on_begin_turn()
{
	typedef std::vector< t_adventure_object_ptr >::iterator t_object_iter;
	t_object_iter object_end = m_owned_objects.end();
	t_object_iter object_iter = m_owned_objects.begin();
	for ( ; object_iter != object_end; ++object_iter )
		( *object_iter )->on_begin_turn();
}

// ------------------------------------------------------------------
// ------------------------------------------------------------------
void t_player::on_end_turn()
{
	typedef std::vector< t_adventure_object_ptr >::iterator t_object_iter;
	t_object_iter object_end = m_owned_objects.end();
	t_object_iter object_iter = m_owned_objects.begin();
	for ( ; object_iter != object_end; ++object_iter )
		( *object_iter )->on_end_turn();
}

// ------------------------------------------------------------------
// ------------------------------------------------------------------
void t_player::ai_spend_discretionary( t_material_array const& amount )
{
	spend( amount );
	m_ai_discretionary_funds -= amount;
}

void t_player::add_lighthouse( t_lighthouse* )
{
	m_lighthouse_count++;
}

void t_player::remove_lighthouse( t_lighthouse* )
{
	m_lighthouse_count--;
}

// ------------------------------------------------------------------
// material array
// ------------------------------------------------------------------
bool t_material_array::write( std::streambuf& buffer ) const
{
	::write( buffer, m_data, sizeof( m_data ));
	return true;
}

// ------------------------------------------------------------------
// material array
// ------------------------------------------------------------------
bool t_material_array::read( std::streambuf& buffer ) 
{
	::read( buffer, m_data, sizeof( m_data ));
	return true;
}

namespace
{
	// Data for enemy_point_filterer()
	struct t_enemy_point_filterer_data
	{
		t_adventure_enemy_marker &	m_enemy_data;
		bool *						m_enemy_adjacent_towns_found;

		t_enemy_point_filterer_data( t_adventure_enemy_marker & enemy_data,
									 bool *					    enemy_adjacent_towns_found )
			: m_enemy_data( enemy_data ), m_enemy_adjacent_towns_found( enemy_adjacent_towns_found )
		{
		}
	};

	// ------------------------------------------------------------------
	// Filter handler for t_adventure_object::find_adjacent_space. 
	// Filters out points adjacent to enemies.
	// ------------------------------------------------------------------
	void enemy_point_filterer( t_adv_map_point const & point, bool & filter_out, t_enemy_point_filterer_data & data )
	{
		if ( data.m_enemy_data.get_existing_data( point ).actual_enemy_adjacent )
		{
			filter_out = true;

			if ( data.m_enemy_adjacent_towns_found )
				*data.m_enemy_adjacent_towns_found = true;
		}
	}
}

// ------------------------------------------------------------------
// find the town belonging to the specified player or his allies
// which is closest to the given point.
// NOTE:  Special case for boats in here ASSUMES that you'll convert
// the boat into a land army before placing in on the returned location.
// ------------------------------------------------------------------
t_town* find_nearest_town(	t_adventure_map *		map,
							t_player*				player,
							t_adv_map_point const&	location,
							t_adv_map_point &		target_location,
							t_creature_array *      army,
							t_town const*			excluded_town,
							bool *                  enemy_adjacent_towns_found,
							bool *					blocked_towns_found,
							bool					check_enemy_adjacency,
							int						minimum_distance,
							bool*					nearby_town_found )
{
	t_creature_array	temp_land_army;

	assert( player != 0 );
	assert( map != 0 );

	t_adventure_enemy_marker enemy_marker( *map ); // Needed for enemy adjacency testing

	minimum_distance *= minimum_distance;
	if ( army )
	{
		// Since find_adjacent_space used below only considers water cells for boats, but when
		// we come in through this function we actually intend to convert the army into a land
		// army, we'll use a temporary "probe" land army to fool find_adjacent_space.
		if ( army->is_boat() )
		{
			army = &temp_land_army;
		}

		enemy_marker.set_army( army );
		enemy_marker.initialize();
	}

	t_town*				 closest = 0;
	int					 closest_distance;

	if ( enemy_adjacent_towns_found )
		*enemy_adjacent_towns_found = false;

	if ( blocked_towns_found )
		*blocked_towns_found = false;
	if (nearby_town_found)
		*nearby_town_found = false;

	int cur_player_number;
	for ( cur_player_number = 0; cur_player_number < map->get_player_count(); cur_player_number++ )
	{
		t_player & cur_player = map->get_player( cur_player_number );

		if ( !same_team( &cur_player, player ) )
			continue;

		t_town_list&			towns = *cur_player.get_towns();
		int						i;
		int						distance;
		t_level_map_point_2d	delta;
		t_adv_map_point			trigger_point;
		t_adv_map_point			town_space;

		for (i = 0; i < towns.size(); i++)
		{
			if (towns[i].get() == excluded_town)
				continue;
			towns[i]->get_trigger_cell( trigger_point );
			delta = location - trigger_point;
			distance = delta.column * delta.column + delta.row * delta.row
					   + abs( delta.level << 16 );
			if (distance < minimum_distance)
			{
				if (nearby_town_found != 0)
					*nearby_town_found = true;
				continue;
			}
			if (closest != 0 && distance > closest_distance)
				continue;

			// Make sure there is room for this army
			if ( army )
			{
				bool enemy_adjacent = false;
				t_adventure_object::t_find_space_filter_handler filter;
				t_enemy_point_filterer_data filter_data( enemy_marker, 
														 &enemy_adjacent );

				// Reconfigure our filter function handler if we need one,
				// otherwise the default (NULL) function will be used.
				if (check_enemy_adjacency)
					filter = add_3rd_argument< t_enemy_point_filterer_data & >( function_3_handler( enemy_point_filterer ), filter_data );

				if ( !towns[i]->find_adjacent_space( town_space, *army, filter ) )
				{
					if ( enemy_adjacent )
					{
						if ( enemy_adjacent_towns_found )
							*enemy_adjacent_towns_found = true;
					}
					else
					{
						if ( blocked_towns_found )
							*blocked_towns_found = true;
					}
					continue;
				}
			}

			closest = towns[i];
			closest_distance = distance;
			target_location = town_space;
		}
	}

	return closest;
}

void t_player::force_elimination()
{
	m_force_elimination = true;
}

void t_player::eliminate()
{
	m_eliminated = true;
}

// ---------------------------------------------------------------------
// based on the battle odds, return victory points
// ---------------------------------------------------------------------
int calculate_battle_victory_points( t_creature_array const &	victor, 
									 t_creature_array const &	defeated,	
									 t_creature_array const &	victor_losses,
									 t_creature_array const &   defeated_losses,
									 t_map_size					map_size )
{
	enum t_ratio_ids {
		k_1_to_3 = 0,
		k_1_to_2,
		k_2_to_3,
		k_1_to_1,
		k_3_to_2,

		k_num_ratios
	};

	static int const k_battle_points[k_map_size_count][k_num_ratios] = 
	{ 
		{ 60, 40, 30, 20, 10 },	// Small
		{ 45, 30, 25, 15,  8 },	// Medium
		{ 30, 20, 15, 10,  5 },	// Large
		{ 30, 20, 10,  8,  3 }	// Extra Large
	};

	float victor_value = victor.get_experience_value() + victor_losses.get_experience_value();
	float defeated_value = defeated.get_experience_value() + defeated_losses.get_experience_value();

	if ( defeated_value <= 0 )
		return 0;

	// create ratio index
	t_ratio_ids index;

	float ratio = victor_value / defeated_value;

	if ( ratio <= 0.333f )		// 1:3
		index = k_1_to_3; 
	else if( ratio <= 0.5f )	// 1:2
		index = k_1_to_2;
	else if( ratio <= 0.666f )	// 2:3
		index = k_2_to_3;
	else if( ratio <= 1.0f )	// 1:1
		index = k_1_to_1;
	else if (ratio <= 1.5f)    // 1:1 -> 3:2
		index = k_3_to_2;
	else
		return 0; // Scott, don't change this again.  Battles where you outnumber
				  // the enemy 3:2 or more don't merit a score, since they're not hard.

	// return the awarded points based on map size and ratio
	assert( index < k_num_ratios );
	assert( map_size < k_map_size_count );
	return k_battle_points[map_size][index];
}

